﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.Drawing.Design;
using ArtMath.Core.Data;
using ArtMath.Attribute;
using ArtMath.Component.Annotation;
using ArtMath.CommonType;
using ArtMath.Setting;
using ArtMath.Data;
using ArtMath.Resource;
using ArtMath.Core.Helper;
using ArtMath.Editor;
using ArtMath.Parameter.Converter;

namespace ArtMath.Parameter
{
    /// <summary>
    /// 参数
    /// </summary>
    [Serializable]
    public class BasicParameter : IRecoverableDelete, ISerializable
    {
        string name, showinpads, showinpads_bkp;
        int precision;
        public int ReferenceCount = 0;
        [Browsable(false)]
        public ObjectState ObjectState { get; private set; } = ObjectState.Normal;
        ParameterUnit unit = Settings.Instance.ParamUnit;
        DocStringX start, step, stop, value;
        [NonSerialized]
        ListViewItem parametersManagerItem;
        [Browsable(false)]
        public ListViewItem ParametersManagerItem { get { return parametersManagerItem; } }
        [NonSerialized]
        ListViewItem recycleItem;
        [Browsable(false)]
        public ListViewItem RecycleItem { get { return recycleItem; } }
        [Browsable(false)]
        public List<ReadOnlyText> Texts { get; } = new List<ReadOnlyText>();
        public event Action<BasicParameter, ValueChangedEventArgs> ValueChanged;
        [Browsable(false)]
        public Document Document { get; }
        public event Action<BasicParameter> Recovered;
        public BasicParameter(Document doc, string name, string value, string start, string step, string stop)
        {
            Document = doc;
            InitialValue = new DocStringX(Document, start);
            Step = new DocStringX(Document, step);
            TerminalValue = new DocStringX(Document, stop);
            Accuracy = 2;
            parametersManagerItem = new ListViewItem(new string[] { name, string.Empty })
            {
                Tag = this,
                ImageIndex = -1,
            };
            Name = name;
            Value = new DocStringX(Document);
            Value.SetExpression(value);
            Document.Parameters.Add(this);
        }
        [Browsable(false)]
        public virtual bool CanRecover
        {
            get { return ObjectState == ObjectState.Deleted; }
        }
        [MultilingualCategory("Basic"), MultilingualDisplayName("Name")]
        public string Name
        {
            get { return name; }
            set
            {
                if (string.IsNullOrEmpty(value))
                {
                    MessageBox.Show(Resources.Translate("NameCannotBeEmpty"), Resources.Translate("Error"), MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                else
                {
                    if (Document.CheckParamNameExist(value))
                    {
                        if (MessageBox.Show(string.Format(Resources.Translate("ParameterNameExistInDocument"), value), Resources.Translate("Ask"), MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question) == DialogResult.Yes)
                        {
                            value = Utility.GetNextAvailableName(value, Document.CheckParamNameExist);
                        }
                        else
                        {
                            return;
                        }
                    }
                    name = value;
                    if (RecycleItem != null)
                    {
                        RecycleItem.SubItems[0].Text = name;
                    }
                    foreach (ReadOnlyText item in Texts)
                    {
                        item.Name = name;
                    }
                    ParametersManagerItem.Text = name;
                    refreshText();
                }
            }
        }
        void refreshText()
        {
            foreach (ReadOnlyText item in Texts)
            {
                double showVal = Value.Value;
                string suffix = string.Empty;
                switch (item.Pad.AngleUnit)
                {
                    case AngleUnit.Radian:
                        switch (Unit)
                        {
                            case ParameterUnit.Degree:
                                showVal *= Angle.DToR;
                                break;
                        }
                        break;
                    case AngleUnit.Degree:
                        switch (Unit)
                        {
                            case ParameterUnit.Radian:
                                showVal *= Angle.RToD;
                                suffix = "°";
                                break;
                            case ParameterUnit.Degree:
                                suffix = "°";
                                break;
                        }
                        break;
                }
                item.InnerText.Text = $"{name} = {showVal}{suffix}";
                item.Pad.Canvas.Invalidate();
            }
        }
        [MultilingualCategory("Basic"), MultilingualDisplayName("Value")]
        public virtual DocStringX Value
        {
            get { return value; }
            set
            {
                if (this.value is object)
                {
                    this.value.ParametersValueChanged -= Value_ParametersValueChanged;
                    this.value.ExpressionSet -= Value_ExpressionSet;
                }
                this.value = value;
                if (this.value is object)
                {
                    this.value.ParametersValueChanged += Value_ParametersValueChanged;
                    this.value.ExpressionSet += Value_ExpressionSet;
                }
            }
        }
        void Value_ParametersValueChanged(DocStringX sender, ParametersValueChangedEventArgs e)
        {
            valueChanged(e.OldValue);
        }
        void Value_ExpressionSet(StringX sender, ExpressionSetEventArgs e)
        {
            if (!valueChanged(e.OldValue))
            {
                MessageBox.Show(Resources.Translate("ValueOutOfRange"), Resources.Translate("Error"), MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        bool valueChanged(double oldValue)
        {
            if (Value >= InitialValue && Value <= TerminalValue)
            {
                Value.Value = Math.Round(Value.Value, Accuracy);
                ParametersManagerItem.ListView.CrossThreadInvoke(() => { ParametersManagerItem.SubItems[1].Text = Value.Value + (Unit == ParameterUnit.Degree ? "°" : string.Empty); });
                refreshText();
                ValueChanged?.Invoke(this, new ValueChangedEventArgs(oldValue));
                return true;
            }
            return false;
        }
        [MultilingualCategory("Basic"), DefaultValue(typeof(ParameterUnit), "None"), MultilingualDisplayName("Unit"), TypeConverter(typeof(ParameterUnitConverter))]
        public virtual ParameterUnit Unit
        {
            get { return unit; }
            set
            {
                unit = value;
                refreshText();
            }
        }
        [MultilingualCategory("Basic"), MultilingualDisplayName("Step")]
        public virtual DocStringX Step
        {
            get { return step; }
            set { step = value; }
        }
        [MultilingualCategory("Basic"), MultilingualDisplayName("InitialValue")]
        public virtual DocStringX InitialValue
        {
            get { return start; }
            set { start = value; }
        }
        [MultilingualCategory("Basic"), MultilingualDisplayName("TerminalValue")]
        public virtual DocStringX TerminalValue
        {
            get { return stop; }
            set { stop = value; }
        }
        [MultilingualCategory("Basic"), DefaultValue(typeof(int), "2"), MultilingualDisplayName("Accuracy")]
        public int Accuracy
        {
            get { return precision; }
            set
            {
                if (value < 0 || value > 15)
                {
                    MessageBox.Show(Resources.Translate("Accuracy") + " ∈ [0, 15]", Resources.Translate("Exclamation"), MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    return;
                }
                precision = value;
            }
        }
        ReadOnlyText getText(SketchPad pad)
        {
            foreach (ReadOnlyText item in Texts)
            {
                if (item.Pad == pad)
                {
                    return item;
                }
            }
            return null;
        }
        [MultilingualCategory("View"), MultilingualDisplayName(null, "Sketchpad", "Display"), Editor(typeof(SketchpadEditor), typeof(UITypeEditor))]
        public string ShowInPads
        {
            get { return showinpads; }
            set
            {
                if (value == null) return;
                showinpads = value;
                HashSet<string> padNames = new HashSet<string>(showinpads.Split(';', '；'));
                foreach (SketchPad pad in Document.Pads)
                {
                    ReadOnlyText rot = getText(pad);
                    if (padNames.Contains(pad.Name))
                    {
                        if (rot == null)
                        {
                            rot = new ReadOnlyText(pad, name, string.Empty);
                            pad.AngleUnitChanged += refreshText;
                            rot.InnerText.Font = Settings.Instance.ParamFont;
                            Texts.Add(rot);
                            refreshText();
                            rot.SpareCoordinate_ = pad.ParameterLocationManager.PutLabel(SizeD.FromSizeF(rot.ActualSize));
                            rot.CreateComponents();
                        }
                    }
                    else
                    {
                        if (rot != null)
                        {
                            pad.AngleUnitChanged -= refreshText;
                            rot.Delete();
                            Texts.Remove(rot);
                        }
                    }
                }
            }
        }
        public virtual void Delete(bool recoverable = false)
        {
            if (ReferenceCount > 0)
            {
                throw new InvalidOperationException("ParameterInUse");
            }
            if (recoverable)
            {
                if (RecycleItem == null)
                {
                    recycleItem = new ListViewItem(new string[] { Name, Resources.Translate("Parameter") }) { Tag = this };
                }
                showinpads_bkp = ShowInPads;
                UIInteraction.RecycleBin.Delete(this);
                ObjectState = ObjectState.Deleted;
            }
            else
            {
                if (RecycleItem != null)
                {
                    RecycleItem.Remove();
                }
                Document.Parameters.Remove(this);
                ObjectState = ObjectState.Disposed;
            }
            ShowInPads = string.Empty;
            ParametersManagerItem.Remove();
        }
        public virtual void Recover()
        {
            if (Document.CheckParamNameExist(Name))
            {
                DialogResult res = MessageBox.Show(string.Format(Resources.Translate("ParameterNameExistInDocument"), value), Resources.Translate("Ask"), MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
                switch (res)
                {
                    case DialogResult.Yes:
                        Name = Utility.GetNextAvailableName(Name, Document.CheckParamNameExist);
                        break;
                    case DialogResult.No:
                        return;
                    case DialogResult.Cancel:
                        throw new OperationCanceledException();
                }
            }
            ShowInPads = showinpads_bkp;
            RecycleItem.Remove();
            Recovered?.Invoke(this);
            ObjectState = ObjectState.Normal;
        }

        public void BeforeSerializing(Dictionary<string, object> data)
        {

        }

        public void AfterDeserialized(Dictionary<string, object> data)
        {
            parametersManagerItem = new ListViewItem(new string[] { Name, Value.Value + (Unit == ParameterUnit.Degree ? "°" : string.Empty) })
            {
                Tag = this,
                ImageIndex = -1,
            };
            if (ObjectState == ObjectState.Deleted)
            {
                recycleItem = new ListViewItem(new string[] { Name, Resources.Translate("Parameter") }) { Tag = this };
            }
        }
    }
    public class ValueChangedEventArgs : EventArgs
    {
        public double OldValue { get; }
        public ValueChangedEventArgs(double oldValue)
        {
            OldValue = oldValue;
        }
    }
    //public class PointMovePrepareEventArgs : EventArgs
    //{
    //    public ShapeBase Sender { get; }
    //    public PointMovePrepareEventArgs(ShapeBase sender = null)
    //    {
    //        Sender = sender;
    //    }
    //}
    //public class PointMoveCleanEventArgs : EventArgs
    //{
    //    public ShapeBase Sender { get; }
    //    public PointMoveCleanEventArgs(ShapeBase sender = null)
    //    {
    //        Sender = sender;
    //    }
    //}
    //public class PointMoveEventArgs : EventArgs
    //{
    //    public PointD Location { get; }
    //    public bool IsComplusive { get; }
    //    public Base Sender { get; }
    //    public PointMoveEventArgs(PointD location, bool isCompulsive, Base sender = null)
    //    {
    //        Location = location;
    //        IsComplusive = isCompulsive;
    //        Sender = sender;
    //    }
    //}
}
